package com.fzy.core;

import java.io.Serializable;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.fzy.core.entity.EntityCreatedDate;
import com.fzy.core.entity.EntityCreatedDateString;
import com.fzy.core.entity.EntityCreatedUser;
import com.fzy.core.entity.EntityCreatedUserString;
import com.fzy.core.entity.EntityId;
import com.fzy.core.entity.EntityIdString;
import com.fzy.core.entity.EntityStatus;
import com.fzy.core.entity.EntityUpdatedDate;
import com.fzy.core.entity.EntityUpdatedDateString;
import com.fzy.core.entity.EntityUpdatedUser;
import com.fzy.core.entity.EntityUpdatedUserString;
import com.fzy.core.entity.Page;
import com.fzy.core.spring.MyColumnMapRowMapper;
import com.fzy.core.spring.MyPreparedStatementCreator;
import com.fzy.core.spring.MyPreparedStatementSetter;
import com.fzy.core.spring.MyRowMapperResultSetExtractor;
import com.fzy.core.utils.DateUtils;
import com.fzy.core.utils.SqlUtils;
import com.fzy.core.utils.StringUtils;
import com.fzy.core.utils.U;

public class BaseService {

	private static final Logger log = LoggerFactory.getLogger(BaseService.class);
	public static final int DEFAULT_SEQUENCE_LENGTH = 10;
	protected SessionFactory sessionFactory;
	protected JdbcTemplate jdbcTemplate;

	public Object get(Class clazz, Serializable id) {
		Object result = sessionFactory.getCurrentSession().get(clazz, id);
		log.debug("PARAMETERS:");
		log.debug("ID:[" + id + "]");
		return result;
	}
	
	public Object getByIdString(Class clazz, String id){
		return getByIdString(clazz, "id", id);
	}
	
	public Object getByIdString(Class clazz, String idProperty, String id){
		Criteria criteria = getSession().createCriteria(clazz);
		criteria.add(Restrictions.eq(idProperty, id));
		Object result = criteria.uniqueResult();
		log.debug("PARAMETERS:");
		log.debug(idProperty + ":[" + id + "]");
		return result;
	}

	public Serializable save(Object entity) {
		return sessionFactory.getCurrentSession().save(entity);
	}

	public void update(Object entity) {
		sessionFactory.getCurrentSession().update(entity);
	}

	public Serializable saveEntityIdString(EntityIdString entity) {
		String id = entity.getId();
		// is creating entity
		boolean isCreated = id == null;
		return saveEntity(entity, isCreated);
	}

	public Serializable saveEntityId(EntityId entity) {
		Long id = entity.getId();
		// is creating entity
		boolean isCreated = id == null;
		return saveEntity(entity, isCreated);
	}

	public Serializable saveEntity(Object entity) {
		Object id = null;
		if(entity instanceof EntityId)
			id = ((EntityId)entity).getId();
		if(entity instanceof EntityIdString)
			id = ((EntityIdString)entity).getId();
		// is creating entity
		boolean isCreated = id == null;
		return saveEntity(entity, isCreated);
	}
	
	public Serializable saveEntity(Object entity, boolean isCreated){
		// record fields
		if(isCreated){
			// status field
			if (entity instanceof EntityStatus) {
				EntityStatus entityStatus = (EntityStatus) entity;
				entityStatus.setStatus("1");
			}
			
			if (entity instanceof EntityCreatedUser){
				((EntityCreatedUser)entity).setCreatedUser(U.getUser());
			}
			if (entity instanceof EntityCreatedUserString){
				((EntityCreatedUserString)entity).setCreatedUser(U.getUserString());
			}
			if (entity instanceof EntityCreatedDate){
				((EntityCreatedDate)entity).setCreatedDate(new Date());
			}
			if (entity instanceof EntityCreatedDateString){
				((EntityCreatedDateString)entity).setCreatedDate(DateUtils.getDateLongPattern());
			}
		}else{
			if (entity instanceof EntityUpdatedUser){
				((EntityUpdatedUser)entity).setUpdatedUser(U.getUser());
			}
			if (entity instanceof EntityUpdatedUserString){
				((EntityUpdatedUserString)entity).setUpdatedUser(U.getUserString());
			}
			if (entity instanceof EntityUpdatedDate){
				((EntityUpdatedDate)entity).setUpdatedDate(new Date());
			}
			if (entity instanceof EntityUpdatedDateString){
				((EntityUpdatedDateString)entity).setUpdatedDate(DateUtils.getDateLongPattern());
			}
		}

		if (isCreated)
			return save(entity);
		else{
			update(entity);
			return null;
		}
	}

	public List queryForList(String sql) {
		return queryForList(sql, null);
	}
	
	public Map queryForMap(String sql) {
		return queryForMap(sql, null);
	}

	public Map queryForMap(String sql, Object[] params) {
		logSqlDebug(sql, params);
		try {
			return (Map) jdbcTemplate.queryForObject(sql, params, new MyColumnMapRowMapper());
		} catch (Exception e) {
			logSqlError(e, sql, params);
		}
		return null;
	}

	public List queryForList(String sql, Object[] params) {
		logSqlDebug(sql, params);
		try {
			return queryForListWithLowercaseKey(sql, params);
		} catch (Exception e) {
			logSqlError(e, sql, params);
		}
		return new LinkedList();
	}
	
	protected List queryForListWithLowercaseKey(String sql, Object[] params) {
		return query(sql, params, new MyColumnMapRowMapper());
	}

	public void execute(String sql) {
		logSqlDebug(sql, null);
		try {
			jdbcTemplate.execute(sql);
		} catch (Exception e) {
			logSqlError(e, sql, null);
		}
	}

	public List query(String sql, RowMapper rowMapper) {
		logSqlDebug(sql, null);
		try {
			return jdbcTemplate.query(sql, rowMapper);
		} catch (Exception e) {
			logSqlError(e, sql, null);
		}
		return new LinkedList();
	}

	public List query(String sql, Object[] params, RowMapper rowMapper) {
		List result = null;
		logSqlDebug(sql, params);
		try {
			result = jdbcTemplate.query(sql, params, rowMapper);
		} catch (Exception e) {
			logSqlError(e, sql, params);
		}
		return result == null ? new LinkedList() : result;
	}

	public Page queryPage(String sql, Object[] params,
			Page page) {
		return queryPage(sql, params, new MyColumnMapRowMapper(), page);
	}

	public Page queryPage(String sql, Object[] params, RowMapper mapper,
			Page page) {
		logSqlDebug(sql, params);
		try {
			return (Page) jdbcTemplate.query(
					new MyPreparedStatementCreator(sql),
					new MyPreparedStatementSetter(params),
					new MyRowMapperResultSetExtractor(mapper, page));
		} catch (Exception e) {
			logSqlError(e, sql, params);
		}
		return page;
	}

	public int updateSql(String sql) {
		return updateSql(sql, new Object[] {});
	}

	public int updateSql(String sql, List params) {
		return updateSql(sql, params == null ? null : params.toArray());
	}

	public int updateSql(String sql, Object[] params) {
		logSqlInfo(sql, params);
		int num = 0;
		try {
			num = jdbcTemplate.update(sql, params);
		} catch (Exception e) {
			logSqlError(e, sql, params);
		}
		return num;
	}

	public Object queryForFirst(String sql, Object[] params, RowMapper mapper) {
		Object result = null;
		try {
			List l = jdbcTemplate.query(sql, params, mapper);
			if (l.size() > 0)
				result = l.get(0);
		} catch (Exception e) {
			logSqlError(e, sql, params);
		}
		return result;
	}

	public String getSequenceString(String sequenceName) throws Exception {
		return getSequenceString(sequenceName, DEFAULT_SEQUENCE_LENGTH);
	}

	public String getSequenceString(String sequenceName, int length) throws Exception {
		long seq = getSequence(sequenceName);
		return U.sequence2string(seq, length);
	}

	protected String getSqlAndSetParams(List params, String sql, Object param) {
		String result = "";
		if (param != null && !"".equals(param.toString().trim())) {
			result += sql;
			if (param instanceof String) {
				param = ((String) param).trim();
			}
			params.add(param);
		}
		return result;
	}

	protected String getOrderSql(String fieldName, String columnName,
			Page page) {
		String result = "";
		if (!StringUtils.isBlank(page.getOrderBy())) {
			if (fieldName.equalsIgnoreCase(page.getOrderBy())) {
				if (Page.DESC.equalsIgnoreCase(page.getOrder())) {
					result += " order by " + columnName + " desc";
				}
				if (Page.ASC.equalsIgnoreCase(page.getOrder())) {
					result += " order by " + columnName + " asc";
				}
			}
		}
		return result;
	}

	protected String getConditionSqlDateBeginEnd(String beginDateString,
			String endDateString, String columnName, List params) {
		Date beginDate = DateUtils.parseDateSafely(beginDateString, DateUtils.PATTERN_SHOW);
		Date endDate = DateUtils.parseConditionEndDate(endDateString);
		return getConditionSqlDateBeginEnd(beginDate, endDate, columnName,
				params);
	}

	protected String getConditionSqlDateBeginEnd(Date beginDate, Date endDate,
			String columnName, List params) {
		String result = "";
		if (StringUtils.isBlank(columnName))
			return result;
		if (beginDate != null) {
			result += " and " + columnName + " >= ?";
			params.add(DateUtils.format(beginDate, DateUtils.PATTERN_LONG));
		}
		if (endDate != null) {
			result += " and " + columnName + " <= ?";
			params.add(DateUtils.format(endDate, DateUtils.PATTERN_LONG));
		}
		return result;
	}

	protected String getConditionSql(Object value, String condition, List params) {
		return getConditionSql(value, condition, params, false);
	}
	
	/**ANT方式查询，如"*xx"生成"like '%xx'"
	 * @param value
	 * @param condition
	 * @param params
	 * @return
	 */
	protected String getConditionAntLike(String value, String condition,
			List params){
		String result = "";
		if(StringUtils.isBlank(value)){
			return result;
		}
		value = value.trim();
		boolean isLeftLike = false;
		boolean isRightLike = false;
		boolean hasUnderline = false;
		while(value.startsWith("*")){
			value = StringUtils.removeStart(value, "*");
			isLeftLike = true;
		}
		if(StringUtils.isBlank(value)){
			return result;
		}
		while(value.endsWith("*")){
			value = StringUtils.removeEnd(value, "*");
			isRightLike = true;
		}
		if(StringUtils.isBlank(value)){
			return result;
		}
		if(isLeftLike)
			value = likeLeftParam(value);
		if(isRightLike)
			value = likeRightParam(value);
		
		if(isLeftLike || isRightLike){
			result = " and " + condition + " like ?";
			if(value.indexOf('_') > -1){
				hasUnderline = true;
				value = StringUtils.replace(value, "_", "__");
			}
			if(hasUnderline){
				result += " escape '_'";
			}
		}else {
			result = " and " + condition + " = ?";
		}
		params.add(value);
		return result;
	}

	protected String getConditionSql(Object value, String condition,
			List params, boolean isLikeCondition) {
		String result = "";
		boolean valid = false;
		if (value != null) {
			if (value instanceof String) {
				if (!StringUtils.isBlank((String) value)) {
					valid = true;
				}
			} else {
				valid = true;
			}
		}
		if (valid) {
			result = " and " + condition + " ?";
			if (isLikeCondition) {
				params.add(likeParam((String) value));
			} else {
				params.add(value);
			}
		}
		return result;
	}

	protected String likeParam(String param) {
		return "%" + StringUtils.trim(param) + "%";
	}

	protected String likeRightParam(String param) {
		return StringUtils.trim(param) + "%";
	}

	protected String likeLeftParam(String param) {
		return "%" + StringUtils.trim(param);
	}
	public long getSequence(String sequenceName) throws Exception {
		String sql = "select " + SqlUtils.nextval(sequenceName) + " from dual";
		logSqlDebug(sql, null);
		long number = 0;
		try {
			number = jdbcTemplate.queryForLong(sql);
		} catch (Exception e) {
			logSqlError(e, sql, null);
			throw e;
		}
		return number;
	}
	public Long getSequenceLong(String sequenceName) throws Exception {
		return new Long(getSequence(sequenceName));
	}
	
	private void logSqlInfo(String sql, Object[] params){
		if(!log.isInfoEnabled()) return ;
		log.info("SQL:" + sql);
		log.info("PARAMETERS:");
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				log.info("["+String.valueOf(params[i])+"]");
			}
	}
	
	private void logSqlDebug(String sql, Object[] params){
		if(!log.isDebugEnabled()) return ;
		log.debug("SQL:" + sql);
		log.debug("PARAMETERS:");
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				log.debug("["+String.valueOf(params[i])+"]");
			}
	}

	private void logSqlError(Exception e, String sql, Object[] params) {
		log.error("ERROR SQL:" + sql);
		log.error("ERROR PARAMETERS:");
		if (params != null)
			for (int i = 0; i < params.length; i++) {
				log.error("["+String.valueOf(params[i])+"]");
			}
		log.error("", e);
	}
	
	public Criteria createCriteria(Class clazz){
		return sessionFactory.getCurrentSession().createCriteria(clazz);
	}
	
	public Session getSession(){
		return sessionFactory.getCurrentSession();
	}

	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public JdbcTemplate getJdbcTemplate() {
		return jdbcTemplate;
	}

}
